/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ns-2 simple.tcl script (ported from ns-2)
 * Originally authored by Steve McCanne, 12/19/1996
 */

// Port of ns-2/tcl/ex/simple.tcl to ns-3
//
// Network topology
//
//                                  
//       i0 -- n -----           
//         
//           
//       i1 ---   n1            n3 --- h2
//      /  \             |
//     /    ---- n2 ----
//  h1
//
// - all links are point-to-point links with indicated one-way BW/delay
// - FTP/TCP flow from n0 to n3, starting at time 1.2 to time 1.35 sec.
// - DropTail queues 
// - Tracing of queues and packet receptions to file "simple-global-routing.tr"

#include <iostream>
#include <fstream>
#include <string>
#include <cassert>

#include "ns3/core-module.h"
#include "ns3/simulator-module.h"
#include "ns3/node-module.h"
#include "ns3/helper-module.h"
#include "ns3/ipv4-texcp-routing.h"
#include "ns3/texcp-header.h"
#include "ns3/texcp-queue.h"
#include "ns3/point-to-point-net-device.h"

using namespace ns3;

using namespace texcp_routing;

NS_LOG_COMPONENT_DEFINE ("TexcpComparaisonTopology");

std::ofstream asciiFlow;
std::ofstream asciiMetric;

void FlowLog( uint32_t time, Time duration)
{
  asciiFlow << time << " " << duration << '\n';
}

void MetricTrace( double oldVal, double newVal )
{
  asciiMetric << Simulator::Now() << " " << newVal << '\n';
}
 

void LogTime()
{
  NS_LOG_INFO( "Time is " << Simulator::Now() );
  Simulator::Schedule(Seconds(5), MakeEvent( &LogTime));
}

struct IEPair InverseIEPair(struct IEPair s)
{
  struct IEPair is = {s.egressAddr, s.ingressAddr};
  return is;
}

int 
main (int argc, char *argv[])
{
  // Users may find it convenient to turn on explicit debugging
  // for selected modules; the below lines suggest how to do this
#if 1
  LogComponentEnable ("TexcpComparaisonTopology", LOG_LEVEL_INFO);
  //LogComponentEnable("GlobalRoutingHelper", LOG_LEVEL_ALL);
  //LogComponentEnable("Ipv4GlobalRouting", LOG_LEVEL_ALL);
  //LogComponentEnable("Ipv4TexcpRouting", LOG_LEVEL_INFO);
  //LogComponentEnable("TexcpQueue", LOG_LEVEL_INFO);
  //LogComponentEnable("FlowApplication", LOG_LEVEL_ALL);
//  LogComponentEnable("NscTcpSocketImpl", LOG_LEVEL_ALL);
#endif

  // Here, we will explicitly create four nodes.  In more sophisticated
  // topologies, we could configure a node factory.
  NS_LOG_INFO ("Create nodes.");
  NodeContainer h;
  h.Create (6);
  NodeContainer n;
  n.Create (6);
  NodeContainer i;
  i.Create (3);
  NodeContainer e;
  e.Create (3);

  NodeContainer hi0 = NodeContainer (h.Get(0), i.Get (0));
  NodeContainer hi1 = NodeContainer (h.Get(2), i.Get (1));
  NodeContainer hi2 = NodeContainer (h.Get(2), i.Get (2));

  NodeContainer he0 = NodeContainer (h.Get(3), e.Get (0));
  NodeContainer he1 = NodeContainer (h.Get(4), e.Get (1));
  NodeContainer he2 = NodeContainer (h.Get(5), e.Get (2));

  NodeContainer i0n0 = NodeContainer (i.Get(0), n.Get(0));
  NodeContainer i0n1 = NodeContainer (i.Get(0), n.Get(1));
  NodeContainer i1n0 = NodeContainer (i.Get(1), n.Get(0));
  NodeContainer i1n2 = NodeContainer (i.Get(1), n.Get(2));
  NodeContainer i2n1 = NodeContainer (i.Get(2), n.Get(1));
  NodeContainer i2n2 = NodeContainer (i.Get(2), n.Get(2));

  NodeContainer e0n3 = NodeContainer (e.Get(0), n.Get(3));
  NodeContainer e0n4 = NodeContainer (e.Get(0), n.Get(4));
  NodeContainer e1n3 = NodeContainer (e.Get(1), n.Get(3));
  NodeContainer e1n5 = NodeContainer (e.Get(1), n.Get(5));
  NodeContainer e2n4 = NodeContainer (e.Get(2), n.Get(4));
  NodeContainer e2n5 = NodeContainer (e.Get(2), n.Get(5));

  NodeContainer n0n3 = NodeContainer (n.Get(0), n.Get (3));
  NodeContainer n1n4 = NodeContainer (n.Get(1), n.Get (4));
  NodeContainer n2n5 = NodeContainer (n.Get(2), n.Get (5));

  InternetStackHelper texcpStack;
  Ipv4TexcpRoutingHelper texcpRoutingHelper;
  Ipv4GlobalRoutingHelper ipv4GlobalRoutingHelper;
  Ipv4ListRoutingHelper listRouting;
  listRouting.Add (texcpRoutingHelper, 1);
  listRouting.Add (ipv4GlobalRoutingHelper, -1);
  texcpStack.SetRoutingHelper (listRouting);    
  texcpStack.Install (i);
  texcpStack.Install (e);

  InternetStackHelper internet;
  internet.Install (n);
  internet.Install (h);

  std::ostringstream oss;
  //internet.SetTcp ("ns3::NscTcpL4Protocol","Library",StringValue("liblinux2.6.26.so"));
  //NS_LOG_INFO(oss.str());
  
  //internet.Install (h);
  /*Config::Set ("/NodeList/0/$ns3::Ns3NscStack<linux2.6.26>/net.ipv4.tcp_ecn", StringValue ("0"));
  Config::Set ("/NodeList/1/$ns3::Ns3NscStack<linux2.6.26>/net.ipv4.tcp_ecn", StringValue ("0"));
  Config::Set ("/NodeList/2/$ns3::Ns3NscStack<linux2.6.26>/net.ipv4.tcp_ecn", StringValue ("0"));*/

  // We create the channels first without any IP addressing information
  NS_LOG_INFO ("Create channels.");
  PointToPointHelper p2p;

  p2p.SetDeviceAttribute ("DataRate", StringValue ("100Mbps"));
  p2p.SetChannelAttribute ("Delay", StringValue ("2.5ms"));

  NetDeviceContainer dhi0 = p2p.Install (hi0);
  NetDeviceContainer dhi1 = p2p.Install (hi1);
  NetDeviceContainer dhi2 = p2p.Install (hi2);
  NetDeviceContainer dhe0 = p2p.Install (he0);
  NetDeviceContainer dhe1 = p2p.Install (he1);
  NetDeviceContainer dhe2 = p2p.Install (he2);

  p2p.SetQueue("ns3::texcp_routing::TexcpQueue", "DataRate", StringValue("100Mbps"));
  p2p.SetDeviceAttribute ("DataRate", StringValue ("100Mbps"));
  p2p.SetChannelAttribute ("Delay", StringValue ("10ms"));
  
  NetDeviceContainer di0n0 = p2p.Install (i0n0);
  NetDeviceContainer di0n1 = p2p.Install (i0n1);
  NetDeviceContainer di1n0 = p2p.Install (i1n0);
  NetDeviceContainer di1n2 = p2p.Install (i1n2);
  NetDeviceContainer di2n1 = p2p.Install (i2n1);
  NetDeviceContainer di2n2 = p2p.Install (i2n2);

  NetDeviceContainer de0n3 = p2p.Install (e0n3);
  NetDeviceContainer de0n4 = p2p.Install (e0n4);
  NetDeviceContainer de1n3 = p2p.Install (e1n3);
  NetDeviceContainer de1n5 = p2p.Install (e1n5);
  NetDeviceContainer de2n4 = p2p.Install (e2n4);
  NetDeviceContainer de2n5 = p2p.Install (e2n5);

  p2p.SetDeviceAttribute ("DataRate", StringValue ("2Mbps"));
  p2p.SetQueue("ns3::texcp_routing::TexcpQueue", "DataRate", StringValue("2Mbps"));
  NetDeviceContainer l1 = p2p.Install (n0n3);
  p2p.SetDeviceAttribute ("DataRate", StringValue ("15Mbps"));
  p2p.SetQueue("ns3::texcp_routing::TexcpQueue", "DataRate", StringValue("15Mbps"));
  NetDeviceContainer l2 = p2p.Install (n1n4);
  p2p.SetDeviceAttribute ("DataRate", StringValue ("20Mbps"));
  p2p.SetQueue("ns3::texcp_routing::TexcpQueue", "DataRate", StringValue("20Mbps"));
  NetDeviceContainer l3 = p2p.Install (n2n5);  

  Ptr<PointToPointNetDevice> p2pL1 = DynamicCast<PointToPointNetDevice> (l1.Get(0));
  Ptr<TexcpQueue> queueL1 = DynamicCast<TexcpQueue> (p2pL1->GetQueue());
  queueL1->SetOutput ("util-l1.tr");

  Ptr<PointToPointNetDevice> p2pL2 = DynamicCast<PointToPointNetDevice> (l2.Get(0));
  Ptr<TexcpQueue> queueL2 = DynamicCast<TexcpQueue> (p2pL2->GetQueue());
  queueL2->SetOutput ("util-l2.tr");

  Ptr<PointToPointNetDevice> p2pL3 = DynamicCast<PointToPointNetDevice> (l3.Get(0));
  Ptr<TexcpQueue> queueL3 = DynamicCast<TexcpQueue> (p2pL3->GetQueue());
  queueL3->SetOutput ("util-l3.tr");
  
  

  // Later, we add IP addresses.  
  NS_LOG_INFO ("Assign IP Addresses.");
  Ipv4AddressHelper ipv4;
  ipv4.SetBase ("172.0.0.0", "255.255.255.0");
  Ipv4InterfaceContainer ihi0 = ipv4.Assign (dhi0);
  ipv4.SetBase ("172.0.1.0", "255.255.255.0");
  Ipv4InterfaceContainer ihi1 = ipv4.Assign (dhi1);
  ipv4.SetBase ("172.0.2.0", "255.255.255.0");
  Ipv4InterfaceContainer ihi2 = ipv4.Assign (dhi2);
  ipv4.SetBase ("192.0.0.0", "255.255.255.0");
  Ipv4InterfaceContainer ihe0 = ipv4.Assign (dhe0);
  ipv4.SetBase ("192.0.1.0", "255.255.255.0");
  Ipv4InterfaceContainer ihe1 = ipv4.Assign (dhe1);
  ipv4.SetBase ("192.0.2.0", "255.255.255.0");
  Ipv4InterfaceContainer ihe2 = ipv4.Assign (dhe2);

  ipv4.SetBase ("10.0.1.0", "255.255.255.0");
  Ipv4InterfaceContainer ii0n0 = ipv4.Assign (di0n0);
  Ipv4InterfaceContainer ie0n3 = ipv4.Assign (de0n3);
  Ipv4InterfaceContainer ii1n0 = ipv4.Assign (di1n0);
  Ipv4InterfaceContainer ie1n3 = ipv4.Assign (de1n3);
  Ipv4InterfaceContainer il1 = ipv4.Assign (l1);
  ipv4.SetBase ("10.0.2.0", "255.255.255.0");
  Ipv4InterfaceContainer ii0n1 = ipv4.Assign (di0n1);
  Ipv4InterfaceContainer ie0n4 = ipv4.Assign (de0n4);
  Ipv4InterfaceContainer ii2n1 = ipv4.Assign (di2n1);
  Ipv4InterfaceContainer ie2n4 = ipv4.Assign (de2n4);
  Ipv4InterfaceContainer il2 = ipv4.Assign (l2);
  ipv4.SetBase ("10.0.3.0", "255.255.255.0");
  Ipv4InterfaceContainer ii1n2 = ipv4.Assign (di1n2);
  Ipv4InterfaceContainer ie1n5 = ipv4.Assign (de1n5);
  Ipv4InterfaceContainer ii2n2 = ipv4.Assign (di2n2);
  Ipv4InterfaceContainer ie2n5 = ipv4.Assign (de2n5);
  Ipv4InterfaceContainer il3 = ipv4.Assign (l3);


  // Create router nodes, initialize routing database and set up the routing
  // tables in the nodes.
  ipv4GlobalRoutingHelper.PopulateRoutingTables ();

  // Create texcp routes
  Ptr<Ipv4> ipv4i0 = i.Get(0)->GetObject<Ipv4> ();
  Ptr<Ipv4TexcpRouting> staticRoutingi0 = texcpRoutingHelper.GetTexcpRouting (ipv4i0);
  struct IEPair si0 = {ihi0.GetAddress(1), ihe0.GetAddress(1)}; 
  staticRoutingi0->AddNetworkRouteTo (Ipv4Address ("192.0.0.0"), Ipv4Mask ("255.255.255.0"), 2, si0);
  staticRoutingi0->AddNetworkRouteTo (Ipv4Address ("192.0.0.0"), Ipv4Mask ("255.255.255.0"), 3, si0);
  staticRoutingi0->DoStart(1);
  Ptr<Ipv4> ipv4e0 = e.Get(0)->GetObject<Ipv4> ();
  Ptr<Ipv4TexcpRouting> staticRoutinge0 = texcpRoutingHelper.GetTexcpRouting (ipv4e0);
  struct IEPair se0 = InverseIEPair(si0);
  staticRoutinge0->AddNetworkRouteTo (Ipv4Address ("172.0.0.0"), Ipv4Mask ("255.255.255.0"), 2, se0);
  staticRoutinge0->AddNetworkRouteTo (Ipv4Address ("172.0.0.0"), Ipv4Mask ("255.255.255.0"), 3, se0);
  staticRoutinge0->DoStart(1);

  Ptr<Ipv4> ipv4i1 = i.Get(1)->GetObject<Ipv4> ();
  Ptr<Ipv4TexcpRouting> staticRoutingi1 = texcpRoutingHelper.GetTexcpRouting (ipv4i1);
  struct IEPair si1 = {ihi1.GetAddress(1), ihe1.GetAddress(1)}; 
  staticRoutingi1->AddNetworkRouteTo (Ipv4Address ("192.0.1.0"), Ipv4Mask ("255.255.255.0"), 2, si1);
  staticRoutingi1->AddNetworkRouteTo (Ipv4Address ("192.0.1.0"), Ipv4Mask ("255.255.255.0"), 3, si1);
  staticRoutingi1->DoStart(1);
  Ptr<Ipv4> ipv4e1 = e.Get(1)->GetObject<Ipv4> ();
  Ptr<Ipv4TexcpRouting> staticRoutinge1 = texcpRoutingHelper.GetTexcpRouting (ipv4e1);
  struct IEPair se1 = InverseIEPair(si1);
  staticRoutinge1->AddNetworkRouteTo (Ipv4Address ("172.0.1.0"), Ipv4Mask ("255.255.255.0"), 2, se1);
  staticRoutinge1->AddNetworkRouteTo (Ipv4Address ("172.0.1.0"), Ipv4Mask ("255.255.255.0"), 3, se1);
  staticRoutinge1->DoStart(1);

  Ptr<Ipv4> ipv4i2 = i.Get(2)->GetObject<Ipv4> ();
  Ptr<Ipv4TexcpRouting> staticRoutingi2 = texcpRoutingHelper.GetTexcpRouting (ipv4i2);
  struct IEPair si2 = {ihi2.GetAddress(1), ihe2.GetAddress(1)}; 
  staticRoutingi2->AddNetworkRouteTo (Ipv4Address ("192.0.2.0"), Ipv4Mask ("255.255.255.0"), 2, si2);
  staticRoutingi2->AddNetworkRouteTo (Ipv4Address ("192.0.2.0"), Ipv4Mask ("255.255.255.0"), 3, si2);
  staticRoutingi2->DoStart(1);
  Ptr<Ipv4> ipv4e2 = e.Get(2)->GetObject<Ipv4> ();
  Ptr<Ipv4TexcpRouting> staticRoutinge2 = texcpRoutingHelper.GetTexcpRouting (ipv4e2);
  struct IEPair se2 = InverseIEPair(si2);
  staticRoutinge2->AddNetworkRouteTo (Ipv4Address ("172.0.2.0"), Ipv4Mask ("255.255.255.0"), 2, se2);
  staticRoutinge2->AddNetworkRouteTo (Ipv4Address ("172.0.2.0"), Ipv4Mask ("255.255.255.0"), 3, se2);
  staticRoutinge2->DoStart(1);

 
  // Create the OnOff application to send UDP datagrams of size
  // 210 bytes at a rate of 448 Kb/s
  NS_LOG_INFO ("Create Applications.");

  //uint16_t port = 9;   // Discard port (RFC 863)
  /*uint16_t port = 8080;


  NS_LOG_INFO ("Create sink.");
  FlowAppHelper sink (Address (InetSocketAddress (hi0.GetAddress (0), port)));
  sink.SetAttribute ("FlowNum", UintegerValue (0));
  sink.SetAttribute ("Local", AddressValue (InetSocketAddress (Ipv4Address::GetAny (), port)));
  ApplicationContainer apps2 = sink.Install (h.Get (2));
  apps2.Start (Seconds (0.0));
  apps2.Stop (Seconds (100));
 

 // asciiFlow.open("flowlog.tr");
  //asciiMetric.open("metriclog.tr");
  NS_LOG_INFO ("Create flow.");

  FlowAppHelper onoff (Address (InetSocketAddress (hi2.GetAddress (0), port)));
  onoff.SetAttribute ("FlowNum", UintegerValue (1));
 // average flow size of 2MB
  //onoff.SetAttribute ("FlowSize", RandomVariableValue (WeibullVariable (2*1000*1000, 0.5)));
  onoff.SetAttribute ("FlowSize", RandomVariableValue (ConstantVariable (000000)));
  //onoff.SetAttribute ("StatCallback", CallbackValue(MakeCallback( &FlowLog)));
  NodeContainer sources = NodeContainer (h.Get(0), h.Get(1));
  //NodeContainer sources = NodeContainer (h.Get(0));
  ApplicationContainer apps = onoff.Install (sources);
  apps.Start (Seconds (0.0));
  apps.Stop (Seconds (100));*/
  
  UdpEchoServerHelper echoServer (1);
  ApplicationContainer serverApps0 = echoServer.Install(h.Get(3));
  ApplicationContainer serverApps1 = echoServer.Install(h.Get(4));
  ApplicationContainer serverApps2 = echoServer.Install(h.Get(5));
  serverApps0.Start(Seconds(0.1));
  serverApps0.Stop(Seconds (100.0));
  serverApps1.Start(Seconds(0.1));
  serverApps1.Stop(Seconds (100.0));
  serverApps2.Start(Seconds(0.1));
  serverApps2.Stop(Seconds (100.0));

  //Create Client
  UdpEchoClientHelper echoClient0 (ihe0.GetAddress(0),1);
  echoClient0.SetAttribute ("MaxPackets", UintegerValue(9000000));
  echoClient0.SetAttribute ("Interval", TimeValue (Seconds(0.001)));
  echoClient0.SetAttribute ("PacketSize", UintegerValue (1024));

  ApplicationContainer clientApps0 = echoClient0.Install(h.Get (0));
  clientApps0.Start(Seconds (0.2));
  clientApps0.Stop(Seconds (100.0));

  UdpEchoClientHelper echoClient1 (ihe1.GetAddress(0),1);
  echoClient1.SetAttribute ("MaxPackets", UintegerValue(9000000));
  echoClient1.SetAttribute ("Interval", TimeValue (Seconds(0.001)));
  echoClient1.SetAttribute ("PacketSize", UintegerValue (1024));

  ApplicationContainer clientApps1 = echoClient1.Install(h.Get (1));
  clientApps1.Start(Seconds (0.2));
  clientApps1.Stop(Seconds (100.0));

  UdpEchoClientHelper echoClient2 (ihe2.GetAddress(0),1);
  echoClient2.SetAttribute ("MaxPackets", UintegerValue(9000000));
  echoClient2.SetAttribute ("Interval", TimeValue (Seconds(0.001)));
  echoClient2.SetAttribute ("PacketSize", UintegerValue (1024));

  ApplicationContainer clientApps2 = echoClient2.Install(h.Get (2));
  clientApps2.Start(Seconds (20.0));
  clientApps2.Stop(Seconds (80.0));

  //p2p.EnablePcapAll("reflex-simple");
  NS_LOG_INFO ("Run Simulation.");
  LogTime();
  Simulator::Stop (Seconds (100));
  Simulator::Run ();
  TexcpHeader th;
  NS_LOG_INFO ("Done.");

  Simulator::Destroy ();
  return 0;
}
